#include <QString>
#include "ClientPacketListener.h"
#include "externals/includes/osc/OscReceivedElements.h"
#include "externals/includes/osc/OscPacketListener.h"
#include "externals/includes/ip/UdpSocket.h"
#include "stdio.h"
#include "Constants.h"
#include <QDebug>

ClientPacketListener::ClientPacketListener(OSCthulhuClient* client) {
    this->client = client;
}

void ClientPacketListener::ProcessMessage( const osc::ReceivedMessage& m,
                                const IpEndpointName& remoteEndpoint )
    {
        try{
            // example of parsing single messages. osc::OsckPacketListener
            // handles the bundle traversal.        
            if( strcmp( m.AddressPattern(), "/test1" ) == 0 ){
                // example #1 -- argument stream interface
                osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
                bool a1;
                osc::int32 a2;
                float a3;
                const char *a4;
                args >> a1 >> a2 >> a3 >> a4 >> osc::EndMessage;

                qDebug() << "received '/test1' message with arguments: "
                    << a1 << " " << a2 << " " << a3 << " " << a4;

            }else if( strcmp( m.AddressPattern(), "/test2" ) == 0 ){
                // example #2 -- argument iterator interface, supports
                // reflection for overloaded messages (eg you can call
                // (*arg)->IsBool() to check if a bool was passed etc).
                osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
                bool a1 = (arg++)->AsBool();
                int a2 = (arg++)->AsInt32();
                float a3 = (arg++)->AsFloat();
                const char *a4 = (arg++)->AsString();
                if( arg != m.ArgumentsEnd() )
                    throw osc::ExcessArgumentException();

                qDebug() << "received '/test2' message with arguments: "
                    << a1 << " " << a2 << " " << a3 << " " << a4;
            } else if( strcmp( m.AddressPattern(), "/notifyOfLogin" ) == 0 ){
                osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
                const char* userName;
                args >> userName >> osc::EndMessage;
                if(client->getUserName().compare(QString(userName)) ==0) {
                    qDebug() << "Succesfully logged into the Server.";
                } else {
                    qDebug() << "User: " << userName << " logged into the Server.";
                }
                client->connectedToServer();
                //SyncObjectTest
                //QString name = QString("SyncObj")+client->getUserName();
                //client->requestAddSyncObject(name);

            } else if( strcmp( m.AddressPattern(), "/notifyOfLogout" ) == 0 ){
                osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
                const char* userName;
                args >> userName >> osc::EndMessage;
                qDebug() << "User: " << userName << " logged out of the Server.";
                client->removePeer(userName);                

                //SyncObjectTest
                //QString name = QString("SyncObj")+userName;
                //client->requestRemoveSyncObject(name);

            } else if( strcmp( m.AddressPattern(), "/currentUsers" ) == 0 ){
                osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
                while(arg != m.ArgumentsEnd()) {
                    QString uN = QString((arg++)->AsString());
                    if(!client->containsPeer(uN)) {
                        client->addPeer(uN);
                    }
                }
                //client->printUsers();
                if( arg != m.ArgumentsEnd() )
                    throw osc::ExcessArgumentException();
            } else if( strcmp( m.AddressPattern(), "/areYouAlive?" ) == 0 ){
                char buffer[OUTPUT_BUFFER_SIZE];
                osc::OutboundPacketStream p( buffer, OUTPUT_BUFFER_SIZE );
                p << osc::BeginMessage( "/alive" )
                    << qPrintable(client->getServerPassword())
                    << qPrintable(client->getUserName())
                    << qPrintable(client->getUserPassword())
                    << (float)client->getClientPort()
                    << osc::EndMessage;
                client->sendServer(p);
                client->resetAliveTimer();
            } else if( strcmp( m.AddressPattern(), "/ServeraddSyncObject" ) == 0 ){
                osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
                //std::cout << "Message type tags: "<< m.TypeTags() << std::endl;
                int i = 0;
                QString name,type,subtype,argName;
                bool newObject = false;
                while(arg != m.ArgumentsEnd()) {
                    switch(i){
                    case 0:
                        name = QString((arg++)->AsString());
                        break;
                    case 1:
                        type = QString((arg++)->AsString());
                        break;
                    case 2:
                        subtype = QString((arg++)->AsString());
                        newObject = client->addSyncObject(name,type,subtype);
                        break;
                    default :
                            if(newObject) {
                            switch(m.TypeTags()[i]) {
                            case 'i':
                                if(newObject) {
                                    client->addSyncArg(name,new QVariant((int)(arg++)->AsInt32()));
                                }
                                break;
                            case 'f':
                                if(newObject) {
                                    client->addSyncArg(name,new QVariant((arg++)->AsFloat()));
                                }
                                break;
                            case 'd':
                                if(newObject) {
                                    client->addSyncArg(name,new QVariant((arg++)->AsDouble()));
                                }
                                break;
                            case 's':
                                if(newObject) {
                                    client->addSyncArg(name,new QVariant(QString((arg++)->AsString())));
                                }
                                break;
                            }
                        } else {
                            arg++;
                        }
                        break;
                    }
                    i++;
                }
                if(newObject) {
                    client->finalizeSyncObject(name);
                }
                if( arg != m.ArgumentsEnd() )
                    throw osc::ExcessArgumentException();
            } else if( strcmp( m.AddressPattern(), "/addSyncObject" ) == 0 ){
                osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
                char buffer[OUTPUT_BUFFER_SIZE];
                osc::OutboundPacketStream p( buffer, OUTPUT_BUFFER_SIZE );
                p << osc::BeginMessage( "/aO" );
                p << qPrintable(client->getServerPassword());
                int i = 0;
                while(arg != m.ArgumentsEnd()) {
                    switch(m.TypeTags()[i]) {
                    case 'i':
                        p << (int)(arg++)->AsInt32();
                        break;
                    case 'f':
                        p << (arg++)->AsFloat();
                        break;
                    case 'd':
                        p << (arg++)->AsDouble();
                        break;
                    case 's':
                       p << (arg++)->AsString();
                        break;
                    }
                    i++;
                }
                p << osc::EndMessage;
                this->client->sendServer(p);
                if( arg != m.ArgumentsEnd() )
                    throw osc::ExcessArgumentException();
            } else if( strcmp( m.AddressPattern(), "/setSyncArg" ) == 0 ){
                osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();

                char buffer[OUTPUT_BUFFER_SIZE];
                osc::OutboundPacketStream p( buffer, OUTPUT_BUFFER_SIZE );
                p << osc::BeginMessage( "/sA" );
                p << qPrintable(client->getServerPassword());

                QString name = QString((arg++)->AsString());
                p << qPrintable(name);
                int argNum = (arg++)->AsInt32();
                p <<argNum;
                QVariant* argValue;
                switch(m.TypeTags()[2]) {
                    case 'i':
                        argValue = new QVariant((int)(arg++)->AsInt32());
                        p << argValue->toInt();
                        break;
                    case 'f':
                        argValue = new QVariant((arg++)->AsFloat());
                        p << argValue->toFloat();
                        break;
                    case 'd':
                        argValue = new QVariant((arg++)->AsDouble());
                        p << argValue->toDouble();
                        break;
                    case 's':
                        argValue = new QVariant(QString((arg++)->AsString()));
                        p << qPrintable(argValue->toString());
                        break;
                }
                if(arg != m.ArgumentsEnd() ) {
                     if((int)(arg++)->AsInt32() == 1) {
                        p << qPrintable(client->getUserName());
                        client->localSetSyncArg(name,argNum,argValue,remoteEndpoint.port);
                     }
                }

                /*
                int i = 0;
                while(arg != m.ArgumentsEnd()) {
                    switch(m.TypeTags()[i]) {
                    case 'i':
                        if(i!=3) {
                            p << (int)(arg++)->AsInt32();
                        } else {
                            if((int)(arg++)->AsInt32() == 1) {
                                p << qPrintable(client->getUserName());
                                //FINISH THIS NEEDS TO BE DONE FOR LOCAL ARG SETTING
                                //client->localSetSyncArg();
                                qDebug() << remoteEndpoint.port;
                            }
                        }
                        break;
                    case 'f':
                        p << (arg++)->AsFloat();
                        break;
                    case 'd':
                        p << (arg++)->AsDouble();
                        break;
                    case 's':
                        p << (arg++)->AsString();
                        break;
                    }
                    i++;
                }
                */
                p << osc::EndMessage;
                this->client->sendServer(p);
                if( arg != m.ArgumentsEnd() )
                    throw osc::ExcessArgumentException();
            } else if( strcmp( m.AddressPattern(), "/removeSyncObject" ) == 0 ){
                osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();

                char buffer[OUTPUT_BUFFER_SIZE];
                osc::OutboundPacketStream p( buffer, OUTPUT_BUFFER_SIZE );
                p << osc::BeginMessage( "/rO" );
                p << qPrintable(client->getServerPassword());
                int i = 0;
                while(arg != m.ArgumentsEnd()) {
                    switch(m.TypeTags()[i]) {
                    case 'i':
                        p << (int)(arg++)->AsInt32();
                        break;
                    case 'f':
                        p << (arg++)->AsFloat();
                        break;
                    case 'd':
                        p << (arg++)->AsDouble();
                        break;
                    case 's':
                       p << (arg++)->AsString();
                        break;
                    }
                    i++;
                }
                p << osc::EndMessage;
                this->client->sendServer(p);
                if( arg != m.ArgumentsEnd() )
                    throw osc::ExcessArgumentException();
            } else if( strcmp( m.AddressPattern(), "/ServersetSyncArg" ) == 0 ){
                osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
                QString name = QString((arg++)->AsString());
                int argNum = (arg++)->AsInt32();
                switch(m.TypeTags()[2]) {
                case 'i':
                    client->setSyncArg(name,argNum,new QVariant((int)(arg++)->AsInt32()));
                    break;
                case 'f':
                    client->setSyncArg(name,argNum,new QVariant((arg++)->AsFloat()));
                    break;
                case 'd':
                    client->setSyncArg(name,argNum,new QVariant((arg++)->AsDouble()));
                    break;
                case 's':
                    client->setSyncArg(name,argNum,new QVariant(QString((arg++)->AsString())));
                    break;
                }
            } else if( strcmp( m.AddressPattern(), "/ServerremoveSyncObject" ) == 0 ){
                osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
                const char *name;
                args >> name>> osc::EndMessage;
                client->removeSyncObject(name);
            } else if( strcmp( m.AddressPattern(), "/ServerSync" ) == 0 ){
                osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
                int i = 0;
                QString name,type,subtype,argName;
                int mode = 0,count=0;
                client->newStateSync();
                while(arg != m.ArgumentsEnd()) {
                    switch(mode){
                    case 0:
                        name = QString((arg)->AsString());
                        mode = 1;
                        break;
                    case 1:
                        type = QString((arg)->AsString());
                        mode = 2;
                        break;
                    case 2:
                        subtype = QString((arg)->AsString());
                        client->addTempSyncObject(name,type,subtype);
                        mode = 3;
                        break;
                    default :
                            switch(m.TypeTags()[i]) {
                            case 'i':
                                client->addTempSyncArg(name,new QVariant((int)(arg)->AsInt32()));
                                break;
                            case 'f':
                                client->addTempSyncArg(name,new QVariant((float)(arg)->AsFloat()));
                                break;
                            case 'd':
                                client->addTempSyncArg(name,new QVariant((float)(arg)->AsDouble()));
                                break;
                            case 's':
                                if(QString((arg)->AsString()).compare("0nsync") == 0) {
                                    mode = 0;
                                } else if(QString((arg)->AsString()).compare("0esync") == 0){
                                    client->finalizeStateSync();
                                } else {
                                    client->addTempSyncArg(name,new QVariant(QString((arg)->AsString())));
                                }
                                break;
                            }
                        break;
                    }
                    arg++;
                    i++;
                }
                if( arg != m.ArgumentsEnd() )
                    throw osc::ExcessArgumentException();
            } else if( strcmp( m.AddressPattern(), "/sC" ) == 0 ){   // If Multi casting...

                osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
                //qDebug() << "/clientside multicastChat received!" << endl;
                QString uN = QString( arg->AsString() );    // cache username
                while(arg != m.ArgumentsEnd()) {
                    arg++;
                    if( strcmp( (arg)->AsString(), "chatLine" ) == 0 ) {   // Chat Line Multi Cast
                        arg++;
                        client->receiveChat( QString( (arg)->AsString() ) ); // update the GUI with the new convo line
                        break;
                    }
                }
            } else if( strcmp( m.AddressPattern(), "/login" ) == 0 ){
                osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
                QString piece = " ";
                while(arg != m.ArgumentsEnd()) {
                    piece = QString( (arg++)->AsString() );
                }
                //if(piece.compare("none")!=0) {
                    char buffer[OUTPUT_BUFFER_SIZE];
                    osc::OutboundPacketStream p( buffer, OUTPUT_BUFFER_SIZE );
                    p << osc::BeginMessage( "/sChangePiece" );
                    p << qPrintable(client->getServerPassword());
                    p << qPrintable(client->getUserName());
                    p << qPrintable(piece);
                    p << osc::EndMessage;
                    client->sendServer(p);
                //}
                client->appLogin();
            } else if( strcmp( m.AddressPattern(), "/cleanup" ) == 0 ){
                osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
                QString piece = QString( (arg++)->AsString() );
                char buffer[OUTPUT_BUFFER_SIZE];
                osc::OutboundPacketStream p( buffer, OUTPUT_BUFFER_SIZE );
                p << osc::BeginMessage( "/cleanup" );
                p << qPrintable(client->getServerPassword());
                p << qPrintable(client->getUserName());
                p << qPrintable(piece);
                p << osc::EndMessage;
                client->sendServer(p);
            } else if( strcmp( m.AddressPattern(), "/chat" ) == 0 ){

                osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
                QString line = client->getUserName()+": " + (arg++)->AsString();
                client->sendChat(line);
                (&client->mw)->addChatLine(line);
            } else if( strcmp( m.AddressPattern(), "/getChat" ) == 0 ){
                client->getChat();
            } else if( strcmp( m.AddressPattern(), "/changePorts" ) == 0 ){

                osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
                QList<int> appPorts;
                while(arg != m.ArgumentsEnd()) {
                    appPorts.append((int)arg->AsInt32());
                    arg++;
                }
                qDebug() << "Changing appPorts: " << appPorts;
                client->changeAppPorts(appPorts);
            } else if( strcmp( m.AddressPattern(), "/flush" ) == 0 ){
                client->flush();
                qDebug() << "Flushing all objects from client...";
            } else if( strcmp( m.AddressPattern(), "/localSetSyncArg" ) == 0 ){
                osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();

                QString name = QString((arg++)->AsString());
                int argNum = (arg++)->AsInt32();

                char buffer[OUTPUT_BUFFER_SIZE];
                osc::OutboundPacketStream p( buffer, OUTPUT_BUFFER_SIZE );
                p << osc::BeginMessage( "/sA" );
                p << qPrintable(client->getServerPassword());
                p << qPrintable(name);
                p << argNum;
                int iArg;
                float fArg;
                double dArg;
                QString sArg;
                switch(m.TypeTags()[2]) {
                case 'i':
                    iArg = (int)(arg++)->AsInt32();
                    client->setSyncArg(name,argNum,new QVariant(iArg));
                    p<<iArg;
                    break;
                case 'f':
                    fArg = (arg++)->AsFloat();
                    client->setSyncArg(name,argNum,new QVariant(fArg));
                    p<<fArg;
                    break;
                case 'd':
                    dArg = (arg++)->AsDouble();
                    client->setSyncArg(name,argNum,new QVariant(dArg));
                    p<<dArg;
                    break;
                case 's':
                    sArg = QString((arg++)->AsString());
                    client->setSyncArg(name,argNum,new QVariant(sArg));
                    p<<qPrintable(sArg);
                    break;
                }
                p << qPrintable(client->getUserName());
                p << osc::EndMessage;
                this->client->sendServer(p);
                if( arg != m.ArgumentsEnd() )
                    throw osc::ExcessArgumentException();
            } else if( strcmp( m.AddressPattern(), "/changeUserPiece" ) == 0 ){
                osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
                QString user = QString((arg++)->AsString());
                QString piece = QString((arg++)->AsString());
                qDebug() << "Changing user piece: " << user << piece;
                client->changeUserPiece(user,piece);
            }
            client->printMessage(m);
        }catch( osc::Exception& e ){
            // any parsing errors such as unexpected argument types, or
            // missing arguments get thrown as exceptions.
            qDebug() << "error while parsing message: "
                << m.AddressPattern() << ": " << e.what();
        }        
    }
